package com.example.demo.controller;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.example.demo.service.DocService;
import org.elasticsearch.action.bulk.BulkRequest;
import org.elasticsearch.action.bulk.BulkResponse;
import org.elasticsearch.action.delete.DeleteRequest;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.action.update.UpdateRequest;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.core.TimeValue;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.query.RangeQueryBuilder;
import org.elasticsearch.index.query.TermQueryBuilder;
import org.elasticsearch.index.reindex.BulkByScrollResponse;
import org.elasticsearch.index.reindex.DeleteByQueryRequest;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.aggregations.Aggregation;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.Aggregations;
import org.elasticsearch.search.aggregations.bucket.terms.ParsedLongTerms;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.aggregations.bucket.terms.TermsAggregationBuilder;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.sort.FieldSortBuilder;
import org.elasticsearch.search.sort.SortBuilders;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

@RestController
public class DocController {
    private final static Logger log = LoggerFactory.getLogger(DocController.class);

    @Autowired
    private DocService docService;


    /**
     * 新增文档
     * @param indexName
     * @param doc
     * @return
     */
    @PostMapping("/es/doc/add/{indexName}")
    public APIResponse add(@PathVariable String indexName, @RequestBody Map<String, Object> doc) {
        APIResponse apiResponse = new APIResponse();
        apiResponse.setResponseCode(APIResponse.OK);
        try {
            IndexRequest indexRequest = new IndexRequest(indexName);
            String docJson = JSONObject.toJSONString(doc);
            indexRequest.source(docJson, XContentType.JSON);
            docService.add(indexRequest);
        } catch (Exception e) {
            apiResponse.setResponseCode(APIResponse.ERROR);
            log.error("新增文档失败，异常信息：{}", e);
        }
        return apiResponse;
    }

    /**
     * 批量新增文档
     * @param indexName
     * @param docList
     * @return
     */
    @PostMapping("/es/doc/add/batch/{indexName}")
    public APIResponse bulkInsertRecord(@PathVariable String indexName, @RequestBody List<Map<String, Object>> docList) {
        APIResponse apiResponse = new APIResponse();
        apiResponse.setResponseCode(APIResponse.OK);
        try {

            BulkRequest bulkRequest = new BulkRequest();
            docList.forEach(doc -> {
                IndexRequest indexRequest = new IndexRequest(indexName).source(doc, XContentType.JSON);
                bulkRequest.add(indexRequest);
            });
            docService.batch(bulkRequest);
        } catch (Exception e) {
            apiResponse.setResponseCode(APIResponse.ERROR);
            log.error("批量新增文档失败，异常信息：{}", e);
        }
        return apiResponse;
    }

    /**
     * 查询文档
     * @param indexName
     * @return
     */
    @GetMapping("/es/doc/search/{indexName}")
    public APIResponse search(@PathVariable String indexName) {
        APIResponse apiResponse = new APIResponse();
        apiResponse.setResponseCode(APIResponse.OK);
        SearchRequest searchRequest = new SearchRequest(indexName);
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();

//        //精准查询
//        TermQueryBuilder termQueryBuilder = QueryBuilders.termQuery("userName", "userName1");
//        //范围查询
//        RangeQueryBuilder rangeQueryBuilder1 = QueryBuilders.rangeQuery("birthday").from("2020-03-03").to("2020-04-04").format("yyyy-MM-dd");
//        RangeQueryBuilder rangeQueryBuilder2= QueryBuilders.rangeQuery("age").from("10").to("30");
//        //按照年龄排序
//        FieldSortBuilder fieldSortBuilder = SortBuilders.fieldSort("age");
//
//        //多条件查询should == or，must == and
//        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
//        boolQueryBuilder.should(rangeQueryBuilder1).should(rangeQueryBuilder2);
//        sourceBuilder.query(boolQueryBuilder).sort(fieldSortBuilder);
//        sourceBuilder.timeout(new TimeValue(60, TimeUnit.SECONDS));
        searchRequest.source(sourceBuilder);
        try {
            SearchResponse response = docService.search(searchRequest);
            SearchHits hits = response.getHits();
            JSONArray jsonArray = new JSONArray();
//            for (SearchHit hit : hits) {
//                String sourceAsString = hit.getSourceAsString();
//                JSONObject jsonObject = JSON.parseObject(sourceAsString);
//                jsonArray.add(jsonObject);
//            }
            apiResponse.setData(hits);
        } catch (IOException e) {
            apiResponse.setResponseCode(APIResponse.ERROR);
            log.error("查询文档失败，异常信息：{}", e);
        }
        return apiResponse;
    }

    /**
     * 分页查询文档
     * @param indexName
     * @param pageNum
     * @param pageSize
     * @return
     */
    @GetMapping("/es/doc/search/page/{indexName}")
    public APIResponse searchByPage(@PathVariable String indexName, @RequestParam int pageNum, @RequestParam int pageSize) {
        APIResponse apiResponse = new APIResponse();
        apiResponse.setResponseCode(APIResponse.OK);
        SearchRequest searchRequest = new SearchRequest(indexName);
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
        //设置分页
        sourceBuilder.from((pageNum - 1) * pageSize).size(pageSize);
        try {
            searchRequest.source(sourceBuilder);
            SearchResponse response = docService.search(searchRequest);
            SearchHits hits = response.getHits();
            JSONArray jsonArray = new JSONArray();
//            for (SearchHit hit : hits) {
//                String sourceAsString = hit.getSourceAsString();
//                JSONObject jsonObject = JSON.parseObject(sourceAsString);
//                jsonArray.add(jsonObject);
//            }
            apiResponse.setData(hits);
        } catch (IOException e) {
            apiResponse.setResponseCode(APIResponse.ERROR);
            log.error("分页查询文档失败，异常信息：{}", e);
        }
        return apiResponse;
    }

    /**
     * scroll方式查询文档，适合查询大数量场景（高性能）
     * @param indexName
     * @return
     */
    @GetMapping("/es/doc/search/scroll/{indexName}")
    public APIResponse searchByPage(@PathVariable String indexName) {
        APIResponse apiResponse = new APIResponse();
        apiResponse.setResponseCode(APIResponse.OK);
        try {
            SearchRequest searchRequest = new SearchRequest(indexName);
            SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();

            //每次查询1千，直到查询全部数据再返回
            sourceBuilder.size(1000);

            //设置允许获取超过1万条数据，需额外配置ES查询总命中数，不然此设置无效，ES默认只能查1万条
            sourceBuilder.trackTotalHits(true);

            searchRequest.source(sourceBuilder);
            SearchResponse response = docService.search(searchRequest);
            SearchHits hits = response.getHits();
            JSONArray jsonArray = new JSONArray();
//            for (SearchHit hit : hits) {
//                String sourceAsString = hit.getSourceAsString();
//                JSONObject jsonObject = JSON.parseObject(sourceAsString);
//                jsonArray.add(jsonObject);
//            }
            apiResponse.setData(hits);
        } catch (IOException e) {
            apiResponse.setResponseCode(APIResponse.ERROR);
            log.error("srcoll查询文档失败，异常信息：{}", e);
        }
        return apiResponse;
    }


    /**
     * 聚合查询文档
     * @param indexName
     * @return
     */
    @GetMapping("/es/doc/search/agg/{indexName}")
    public APIResponse aggSearch(@PathVariable String indexName) {
        APIResponse apiResponse = new APIResponse();
        apiResponse.setResponseCode(APIResponse.OK);
        SearchRequest searchRequest = new SearchRequest(indexName);
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();

        //按照年龄age字段分组
        TermsAggregationBuilder termsAggregationBuilder = AggregationBuilders.terms("groupby_age").field("age");
        sourceBuilder.aggregation(termsAggregationBuilder);
        sourceBuilder.timeout(new TimeValue(60, TimeUnit.SECONDS));
        searchRequest.source(sourceBuilder);
        try {
            SearchResponse response = docService.search(searchRequest);
            Aggregations aggregations = response.getAggregations();
            Map<String, Aggregation> stringAggregationMap = aggregations.asMap();
            ParsedLongTerms parsedLongTerms = (ParsedLongTerms) stringAggregationMap.get("groupby_age");
            List<? extends Terms.Bucket> buckets = parsedLongTerms.getBuckets();
            Map<Integer, Long> map = new HashMap<>();
            for (Terms.Bucket bucket : buckets) {
                //个数
                long docCount = bucket.getDocCount();
                //年龄
                Number keyAsNumber = bucket.getKeyAsNumber();
                System.out.println(keyAsNumber + "岁的有" + docCount + "个");
                map.put(keyAsNumber.intValue(), docCount);
            }
            apiResponse.setData(map);
        } catch (IOException e) {
            apiResponse.setResponseCode(APIResponse.ERROR);
            log.error("聚合查询文档失败，异常信息：{}", e);
        }
        return apiResponse;
    }

    /**
     * 修改文档
     * @param indexName
     * @param id
     * @param doc
     * @return
     */
    @PostMapping("/es/doc/update/{indexName}/{id}")
    public APIResponse update(@PathVariable String indexName, @PathVariable String id, @RequestBody Map<String, Object> doc) {
        APIResponse apiResponse = new APIResponse();
        apiResponse.setResponseCode(APIResponse.OK);
        try {
            UpdateRequest updateRequest = new UpdateRequest(indexName, id);
            updateRequest.doc(doc);
            docService.update(updateRequest);
        } catch (Exception e) {
            apiResponse.setResponseCode(APIResponse.ERROR);
            log.error("更新文档失败，异常信息：{}", e);
        }
        return apiResponse;
    }

    /**
     * 根据id删除文档
     * @param indexName
     * @param id
     * @return
     */
    @PostMapping("/es/doc/delete/{indexName}/{id}")
    public APIResponse delete(@PathVariable String indexName, @PathVariable String id) {
        APIResponse apiResponse = new APIResponse();
        apiResponse.setResponseCode(APIResponse.OK);
        try {
            DeleteRequest deleteRequest = new DeleteRequest(indexName);
            deleteRequest.id(id);
            docService.delete(deleteRequest);
        } catch (Exception e) {
            apiResponse.setResponseCode(APIResponse.ERROR);
            log.error("删除文档失败，异常信息：{}", e);
        }
        return apiResponse;
    }

    /**
     * 批量删除文档
     * @param indexName
     * @param idList
     * @return
     */
    @PostMapping("/es/doc/delete/batch/{indexName}")
    public APIResponse batchDelete(@PathVariable String indexName, @RequestBody List<String> idList) {
        APIResponse apiResponse = new APIResponse();
        apiResponse.setResponseCode(APIResponse.OK);
        try {
            BulkRequest bulkRequestBuilder = new BulkRequest();
            for (String docId : idList) {
                DeleteRequest deleteRequest = new DeleteRequest(indexName, docId);
                bulkRequestBuilder.add(deleteRequest);
            }
            docService.batch(bulkRequestBuilder);
        } catch (Exception e) {
            apiResponse.setResponseCode(APIResponse.ERROR);
            log.error("批量删除文档失败，异常信息：{}", e);
        }
        return apiResponse;
    }

    /**
     * 根据条件删除文档
     * @param indexName
     * @return
     */
    @PostMapping("/es/doc/delete/byquery/{indexName}")
    public APIResponse DeleteByQuery(@PathVariable String indexName) {
        APIResponse apiResponse = new APIResponse();
        apiResponse.setResponseCode(APIResponse.OK);
        try {
            //删除年龄等于20的所有文档
            DeleteByQueryRequest deleteByQueryRequest = new DeleteByQueryRequest(indexName).setQuery(QueryBuilders.termQuery("age", 20));
            docService.deleteByQuery(deleteByQueryRequest);
        } catch (Exception e) {
            apiResponse.setResponseCode(APIResponse.ERROR);
            log.error("根据条件删除文档失败，异常信息：{}", e);
        }
        return apiResponse;
    }

}
